/****************************************************************************
 * infineon/chips/tc3xx/include/irq.h
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

/* This file should never be included directly but, rather,
 * only indirectly through nuttx/irq.h
 */

#ifndef __ARCH_TRICORE_INCLUDE_TC3XX_IRQ_H
#define __ARCH_TRICORE_INCLUDE_TC3XX_IRQ_H

#include <nuttx/config.h>
#include <IfxCpu_Intrinsics.h>

/* Upper CSA */
#define REG_UPCXI        0
#define REG_PSW          1
#define REG_A10          2
#define REG_UA11         3
#define REG_D8           4
#define REG_D9           5
#define REG_D10          6
#define REG_D11          7
#define REG_A12          8
#define REG_A13          9
#define REG_A14          10
#define REG_A15          11
#define REG_D12          12
#define REG_D13          13
#define REG_D14          14
#define REG_D15          15

/* Lower CSA */
#define REG_LPCXI        0
#define REG_LA11         1
#define REG_A2           2
#define REG_A3           3
#define REG_D0           4
#define REG_D1           5
#define REG_D2           6
#define REG_D3           7
#define REG_A4           8
#define REG_A5           9
#define REG_A6           10
#define REG_A7           11
#define REG_D4           12
#define REG_D5           13
#define REG_D6           14
#define REG_D7           15

#define REG_RA           REG_UA11
#define REG_SP           REG_A10
#define REG_UPC          REG_UA11

#define REG_LPC          REG_LA11

#define TC_CONTEXT_REGS  (16)

#define XCPTCONTEXT_REGS (TC_CONTEXT_REGS)
#define XCPTCONTEXT_SIZE (sizeof(void *) * TC_CONTEXT_REGS)

#define NR_IRQS          (255)

/* PSW: Program Status Word Register */
#define PSW_CDE           (1 << 7) /* Bits 7: Call Depth Count Enable */
#define PSW_IS            (1 << 9) /* Bits 9: Interrupt Stack Control */
#define PSW_IO            (10)     /* Bits 10-11: Access Privilege Level Control (I/O Privilege) */
#define PSW_IO_USER0      (0 << PSW_IO)
#define PSW_IO_USER1      (1 << PSW_IO)
#define PSW_IO_SUPERVISOR (2 << PSW_IO)
#define PSW_GW            (1 << 8)

/* PCXI: Previous Context Information and Pointer Register */
#define PCXI_UL         (1 << 20) /* Bits 20: Upper or Lower Context Tag */
#define PCXI_PIE        (1 << 21) /* Bits 21: Previous Interrupt Enable */
#define PCXI_PIE_BIT    (21) /* Bits 21: Previous Interrupt Enable */
#define PCXI_PIE_MASK   (1 << PCXI_PIE_BIT)

/* FCX: Free CSA List Head Pointer Register */
#define FCX_FCXO        (0)       /* Bits 0-15: FCX Offset Address */
#define FCX_FCXS        (16)      /* Bits 16-19: FCX Segment Address */
#define FCX_FCXO_MASK   (0xffff << FCX_FCXO)
#define FCX_FCXS_MASK   (0xf    << FCX_FCXS)
#define FCX_FREE        (FCX_FCXS_MASK | FCX_FCXO_MASK) /* Free CSA manipulation */

/* ICR: Interrupt Control Register */
#define ICR_IE          (15)         /* Bits 15: Global Interrupt Enable Bit */
#define ICR_PIPN        (16)         /* Bits 16-23: Pending Interrupt Priority Number */
#define ICR_IE_MASK     (0x1 << ICR_IE)
#define ICR_PIPN_MASK   (0xff << ICR_PIPN)

/** Ifx CPUs register */
/** \brief 0x00000100, CPU_PSW_GW */
#define CPU_PSW_GW_MASK ( 0x00000100UL )    /* bit:8 */

/* Tc3xx Customer ID Reg */
#define TC3XX_CPU_CUS_ID 0xFE50
/* Tc3xx Program Status Word Reg */
#define TX3XX_CPU_PSW 0xFE04
/* Tc3xx Previous Context Information Reg */
#define TC3XX_CPU_PCXI 0xFE00
/* Tc3xx Interrupt Control Reg */
#define TC3XX_CPU_ICR 0xFE2C
/* Tc3XX Program Counter Reg */
#define TC3XX_CPU_PC 0xFE08


#ifndef __ASSEMBLY__
struct xcptcontext {
	/* These are saved copies of the context used during
	 * signal processing.
	 */

	uintptr_t *saved_regs;

	/* Register save area with XCPTCONTEXT_SIZE, only valid when:
	 * 1.The task isn't running or
	 * 2.The task is interrupted
	 * otherwise task is running, and regs contain the stale value.
	 */

	uintptr_t *regs;
#if defined(CONFIG_RT_FRAMEWORK ) && (CONFIG_RT_FRAMEWORK == 1)
	uintptr_t *contexthdl;
	uint8_t task_started;
#endif
};

#if defined(CONFIG_RT_FRAMEWORK ) && (CONFIG_RT_FRAMEWORK == 1)
#define CSA_TO_ADDR(csa) ((uintptr_t *)((((csa) & 0x000F0000) << 12) \
                                             | (((csa) & 0x0000FFFF) << 6)))
void up_switch_task_context(void *xcp);
void up_affinity_uart(int irq_prio, int cpu);
int  up_trigger_ipicall(unsigned int cpu);
void up_trigger_ipicalls(unsigned int cpu_mask);
void up_ipicall_attach(void *handler);
bool up_irq_is_enabled(int irq);
void up_init_irq(int irq, int irq_prio);
void up_clear_irq(int irq);
uint32_t up_timer_freq(void);
int up_timer_value(clock_t *ticks);
#if defined(CONFIG_RT_FRAMEWORK_SYSCALL ) && (CONFIG_RT_FRAMEWORK_SYSCALL == 1)
#define SYS_syscall_return       (3)
void syscall_handler(void *syscall_args, uint32_t *regs);
bool is_privilege_mode(void);
void up_syscall(void *syscall_args);

/* called directly */

static inline_function void up_set_privilege_to_user(void)
{
	uintptr_t psw;

	psw = (uintptr_t)__mfcr(TX3XX_CPU_PSW);
	psw &= ~(PSW_IO_USER0 | PSW_IO_USER1 | PSW_IO_SUPERVISOR);
	psw |= PSW_IO_USER0;
	__mtcr(TX3XX_CPU_PSW, psw);
}

/*called by trap*/

static inline_function void up_set_int_stack(uint32_t *regs)
{
	uintptr_t psw;
	uintptr_t pcxi;
	uintptr_t *plcsa, *pucsa;

	plcsa = CSA_TO_ADDR(regs[REG_UPCXI]);
	pcxi = plcsa[REG_LPCXI];
	pucsa = CSA_TO_ADDR(pcxi);

#if defined(__GNUC__) && defined(__TRICORE__)
	__asm volatile
	(
		"mov.a %%a10, %0\n\t"
		"isync\n\t"
		:: "d" (pucsa[REG_A10])
		: "a10", "d0"
	);
#else
	__asm volatile
	(
		"mov.a a10, %0\n\t"
		"isync\n\t"
		:: "d" (pucsa[REG_A10])
		: "a10", "d0"
	);
#endif
	if (0UL == (pucsa[REG_PSW] & PSW_IS)) {
		psw = (uintptr_t)__mfcr(TX3XX_CPU_PSW);
		psw &= ~PSW_IS;
		__mtcr(TX3XX_CPU_PSW, psw);
	}

	if (0UL != (pcxi & PCXI_PIE)) {
		__enable();
	}
}

static inline_function void up_restore_int_stack(uint32_t *regs)
{

}

static inline_function void up_set_user_to_privilege(uint32_t *regs)
{
	uintptr_t *plcsa;
	uintptr_t *pucsa;

	plcsa = CSA_TO_ADDR(regs[REG_UPCXI]);
	pucsa = CSA_TO_ADDR(plcsa[REG_LPCXI]);
	pucsa = CSA_TO_ADDR(pucsa[REG_UPCXI]);

	pucsa[REG_PSW] &= ~(PSW_IO_USER0 | PSW_IO_USER1 | PSW_IO_SUPERVISOR);
	pucsa[REG_PSW] |= PSW_IO_SUPERVISOR;
}

static inline_function void up_set_int_from_syscall(uint32_t *regs)
{
	uintptr_t icr;
	uintptr_t pcxi;
	uintptr_t *plcsa;

	icr = (uintptr_t)__mfcr(TC3XX_CPU_ICR);
	plcsa = CSA_TO_ADDR(regs[REG_UPCXI]);

	pcxi = plcsa[REG_LPCXI];

	pcxi &= ~PCXI_PIE_MASK;
	icr = (icr & ICR_IE_MASK) >> ICR_IE;
	pcxi |= icr << PCXI_PIE_BIT;

	plcsa[REG_LPCXI] = pcxi;
}

#endif /* CONFIG_RT_FRAMEWORK_SYSCALL */
#endif /* CONFIG_RT_FRAMEWORK */

#endif /* __ASSEMBLY__ */

#endif /* __ARCH_TRICORE_INCLUDE_TC3XX_IRQ_H */
